package edu.hcmus.sow.web.document;

import java.io.InputStream;
import java.util.Calendar;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import edu.hcmus.sow.domain.File;
import edu.hcmus.sow.domain.FileAccessRight;
import edu.hcmus.sow.domain.FileRevision;
import edu.hcmus.sow.domain.FileRevisionComment;
import edu.hcmus.sow.domain.Folder;
import edu.hcmus.sow.domain.User;
import edu.hcmus.sow.domain.UserProfile;
import edu.hcmus.sow.domain.repository.FileInfo;
import edu.hcmus.sow.service.DocumentService;
import edu.hcmus.sow.service.RepositoryService;
import edu.hcmus.sow.service.UserService;
import edu.hcmus.sow.utils.SecurityUtil;
import edu.hcmus.sow.utils.StringUtil;

/**
 * Spring MVC controller that handles CRUD requests for File entities
 * 
 */
@Controller
public class FileController {

   // ~ Instance fields
   // ================================================================================================
   @Autowired
   private DocumentService documentService;

   @Autowired
   private RepositoryService repositoryService;

   @Autowired
   private UserService userService;

   // ~ File's methods
   // ================================================================================================
   @RequestMapping(value = "/file/upload", method = RequestMethod.GET)
   public ModelAndView uploads(@RequestParam("p") Integer p) {
      ModelAndView mav = new ModelAndView();

      mav.addObject("p", p);
      mav.setViewName("file_upload");
      return mav;
   }

   @RequestMapping(value = "/file/upload", method = RequestMethod.POST)
   public void upload(@RequestParam("folderID") Integer folderID, HttpServletRequest request) {
      try {
         // Create a new file upload handler
         ServletFileUpload upload = new ServletFileUpload();

         // Parse the request
         FileItemIterator iter = upload.getItemIterator(request);
         while (iter.hasNext()) {
            FileItemStream item = iter.next();
            String name = item.getFieldName();
            InputStream stream = item.openStream();
            if (item.isFormField()) {
               // System.out.println("Form field " + name + " with value " +
               // Streams.asString(stream) + " detected.");
            } else {
               System.out.println("File field " + name + " with file name " + item.getName() + " detected.");

               // Process the input stream
               FileInfo fi = repositoryService.saveFile(stream);
               System.out.println(fi.getRowkey());

               String fileName = item.getName();

               // if file name exists
               if (documentService.findFileByName(fileName) != null) {
                  int index = StringUtils.lastIndexOf(fileName, '.');
                  String temp = fileName.substring(0, index) + "_" + System.currentTimeMillis()
                        + fileName.substring(index);
                  fileName = temp;
               }

               // save file in database
               File file = new File();
               file.setName(fileName);
               Folder foler = documentService.getFolderByID(folderID);
               file.setFolder(foler);

               file = documentService.saveFile(file);

               // get logged in user
               int userID = SecurityUtil.getCurrentLoggedinUserID();
               User user = userService.getUserByID(userID);

               // save revision
               FileRevision fr = new FileRevision();
               fr.setFile(file);
               fr.setName(fileName);
               fr.setSize(fi.getSize());
               fr.setType(item.getContentType());
               fr.setRowkey(fi.getRowkey());
               fr.setRevision(1);
               fr.setCreatedDate(Calendar.getInstance());
               fr.setUserByCreatedBy(user);

               documentService.saveFileRevision(fr);

            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }

      // ModelAndView mav = new ModelAndView();
      // mav.setViewName("repository");
      // return mav;
   }

   @RequestMapping("/file/{id}/download")
   public void download(@PathVariable("id") Integer id, @RequestParam("rk") String rk, HttpServletResponse response) {
      // get file by id
      FileRevision file = documentService.getFileRevisionByID(id);

      // if exist file
      if (file != null) {
         response.setContentType(file.getType());
         response.setContentLength((int) file.getSize().longValue());
         response.setHeader("Content-Disposition", "attachment;filename=\"" + file.getName() + "\"");

         try {
            repositoryService.readFile(file.getRowkey(), response.getOutputStream());
            response.flushBuffer();
         } catch (Exception e) {
            e.printStackTrace();
         }

      }
   }

   @RequestMapping("/file/{id}/delete")
   public String deletefile(@PathVariable("id") Integer id) {
      // get file revision by id
      FileRevision fr = documentService.getFileRevisionByID(id);

      // get file
      File file = fr.getFile();
      // get parent folder id
      Integer folderID = file.getFolderID();

      // delete data on cassandra
      List<FileRevision> filesRevision = documentService.getFilesRevisionByFileID(file.getId());
      for (FileRevision fileRevision : filesRevision) {
         repositoryService.deleteFile(fileRevision.getRowkey());
      }

      // delete file
      documentService.deleteFile(file.getId());

      return "redirect:/folder/" + folderID;
   }

   @RequestMapping("/document/{id}")
   public ModelAndView viewDocument(@PathVariable("id") Integer id) {
      ModelAndView mav = new ModelAndView();

      // get info selected revision
      FileRevision fr = documentService.getFileRevisionByID(id);

      UserProfile user = new UserProfile();
      if (fr.getUserByApprovedBy() != null) {
         user = userService.getUserProfileByUserID(fr.getUserByApprovedBy().getId());
      }

      // get file revisions list
      List<FileRevision> fileRevisions = documentService.getFilesRevisionByFileID(fr.getFile().getId());

      // get comments list
      List<FileRevisionComment> comments = documentService.getCommentsByFileRevisionID(fr.getId());

      mav.addObject("fileRevisions", fileRevisions);
      mav.addObject("comments", comments);
      mav.addObject("file", fr);
      mav.addObject("approver", user);
      mav.setViewName("document_detail");

      return mav;
   }

   @RequestMapping(value = "/ajax/file/{id}/rename", method = RequestMethod.POST)
   @ResponseBody
   public String renameFolder(@PathVariable("id") Integer id, @RequestParam("fileName") String fileName) {
      // get file
      File file = documentService.getFileByID(id);

      // validate
      String message = validate(file.getFolderID(), fileName);

      if (!file.getName().equalsIgnoreCase(fileName.trim())) {
         if (!"".equals(message))
            return message;
      }

      file.setName(fileName.trim());
      // rename file
      documentService.saveFile(file);

      return "";
   }

   @RequestMapping(value = "ajax/file/upload", method = RequestMethod.POST)
   public String ajaxUpload(@RequestParam("currentRevisionID") Integer currentRevisionID, HttpServletRequest request) {
      try {
         // Create a new file upload handler
         ServletFileUpload upload = new ServletFileUpload();

         // Parse the request
         FileItemIterator iter = upload.getItemIterator(request);
         while (iter.hasNext()) {
            FileItemStream item = iter.next();
            // String name = item.getFieldName();
            InputStream stream = item.openStream();
            if (!item.isFormField()) {
               // Process the input stream
               FileInfo fi = repositoryService.saveFile(stream);

               String fileName = item.getName();
               FileRevision currentRevision = documentService.getFileRevisionByID(currentRevisionID);
               // Integer currentFolderID = currentRevision.getFile().getFolderID();

               // get logged in user
               int userID = SecurityUtil.getCurrentLoggedinUserID();
               User user = userService.getUserByID(userID);

               // save revision
               FileRevision fr = new FileRevision();
               fr.copy(currentRevision);
               fr.setId(null);
               fr.setName(fileName);
               fr.setSize(fi.getSize());
               fr.setType(item.getContentType());
               fr.setRowkey(fi.getRowkey());
               fr.setRevision(fr.getRevision() + 1);
               fr.setCreatedDate(Calendar.getInstance());
               fr.setUserByCreatedBy(user);

               fr = documentService.saveFileRevision(fr);

               return "redirect:/document/" + fr.getId();
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
      return "error";
   }

   @RequestMapping(value = "/ajax/file/{id}/addApprover", method = RequestMethod.POST)
   public ModelAndView addApprover(@PathVariable("id") Integer id, @RequestParam("approverID") Integer approverID) {
      ModelAndView mav = new ModelAndView();
      // get file revision
      FileRevision fr = documentService.getFileRevisionByID(id);

      // get user
      UserProfile user = userService.getUserProfileByUserID(approverID);

      // set approve
      fr.setApprovalDueDate(null);
      fr.setApprovalReminder(null);
      fr.setUserByApprovedBy(user.getUserByUserId());
      fr.setApprovalStatus("W");
      // update
      fr = documentService.saveFileRevision(fr);

      mav.addObject("file", fr);
      mav.addObject("approver", user);
      mav.addObject("succes", "succes");
      mav.setViewName("controls/c_approval_form");
      return mav;
   }

   @RequestMapping(value = "/ajax/file/{id}/removeApprover", method = RequestMethod.POST)
   public ModelAndView removeApprover(@PathVariable("id") Integer id) {
      ModelAndView mav = new ModelAndView();
      // get file revision
      FileRevision fr = documentService.getFileRevisionByID(id);

      // set approve
      fr.setApprovalDueDate(null);
      fr.setApprovalReminder(null);
      fr.setUserByApprovedBy(null);
      fr.setApprovalStatus(null);
      fr.setApprovedDate(null);
      fr.setReason(null);
      // update
      fr = documentService.saveFileRevision(fr);

      mav.addObject("file", fr);
      mav.addObject("succes", "succes");
      mav.setViewName("controls/c_approval_form");
      return mav;
   }

   @RequestMapping(value = "/ajax/file/{id}/approveOrRejectFile", method = RequestMethod.POST)
   public ModelAndView approveOrRejectFile(@PathVariable("id") Integer id, @RequestParam("reason") String reason,
         @RequestParam("status") String status) {
      ModelAndView mav = new ModelAndView();
      // get file revision
      FileRevision fr = documentService.getFileRevisionByID(id);

      UserProfile user = userService.getUserProfileByUserID(fr.getUserByApprovedBy().getId());

      // set approval
      fr.setApprovedDate(Calendar.getInstance());
      fr.setReason(reason.trim());
      fr.setApprovalStatus(status.trim());
      // update
      fr = documentService.saveFileRevision(fr);

      mav.addObject("file", fr);
      mav.addObject("approver", user);
      mav.addObject("succes", "succes");
      mav.setViewName("controls/c_approval_form");
      return mav;
   }

   @RequestMapping(value = "/ajax/file/{id}/addComment", method = RequestMethod.POST)
   public ModelAndView addComment(@PathVariable("id") Integer id, @RequestParam("content") String content) {
      ModelAndView mav = new ModelAndView();
      // get file revision
      FileRevision fr = documentService.getFileRevisionByID(id);

      // get logged in user
      int userID = SecurityUtil.getCurrentLoggedinUserID();
      User user = userService.getUserByID(userID);

      FileRevisionComment comment = new FileRevisionComment();
      // set approval
      comment.setFileRevision(fr);
      comment.setContent(content.trim());
      comment.setCreatedDate(Calendar.getInstance());
      comment.setUser(user);
      // save
      comment = documentService.saveFileRevisionComment(comment);

      // get comments list
      List<FileRevisionComment> comments = documentService.getCommentsByFileRevisionID(fr.getId());

      mav.addObject("file", fr);
      mav.addObject("comments", comments);
      mav.setViewName("controls/c_file_comment");
      return mav;
   }

   @RequestMapping(value = "/ajax/document/{id}/permissions", method = RequestMethod.GET)
   public ModelAndView viewPermission(@PathVariable("id") Integer id) {
      ModelAndView mav = new ModelAndView();
      // get file
      File f = documentService.getFileByID(id);

      // get permissions
      List<FileAccessRight> permissions = documentService.getPermissionsByFileID(f.getId());

      mav.addObject("file", f);
      mav.addObject("permissions", permissions);
      mav.setViewName("controls/file_access_right");
      return mav;
   }

   @RequestMapping(value = "/ajax/document/{id}/permission/add", method = RequestMethod.POST)
   public ModelAndView addPermission(@PathVariable("id") Integer id, @RequestParam("newUserIDs") String newUserIDs,
         @RequestParam("newPerm") String newPerm, @RequestParam("pIDs[]") Integer[] pIDs,
         @RequestParam("perms[]") String[] perms) {
      ModelAndView mav = new ModelAndView();

      // update default access right
      File f = documentService.getFileByID(id);
      f.setDefaultAccessRight(perms[0]);
      documentService.saveFile(f);

      // update users's access right
      for (int i = 1; i < pIDs.length; i++) {
         FileAccessRight far = documentService.getPermissionByID(pIDs[i]);
         far.setAccessRight(perms[i]);
         documentService.saveFileAccessRight(far);
      }

      // add new access right
      if (!StringUtils.isEmpty(newUserIDs.trim())) {
         String[] userIDs = newUserIDs.split(",");
         for (int i = 0; i < userIDs.length; i++) {
            User u = userService.getUserByID(Integer.parseInt(userIDs[i]));
            FileAccessRight newFar = documentService.findOrCreatePermission(f.getId(), u.getId());
            newFar.setFile(f);
            newFar.setUser(u);
            newFar.setAccessRight(newPerm);
            documentService.saveFileAccessRight(newFar);
         }
      }

      // view permissions
      mav = viewPermission(id);

      return mav;
   }

   @RequestMapping(value = "/ajax/document/{fileID}/permission/{id}/delete", method = RequestMethod.POST)
   public ModelAndView addPermission(@PathVariable("fileID") Integer fileID, @PathVariable("id") Integer id) {
      ModelAndView mav = new ModelAndView();

      // delete access right
      documentService.deleteFileAccessRight(id);

      // view permissions
      mav = viewPermission(fileID);

      return mav;
   }

   @RequestMapping("/welcome")
   public String printWelcome(HttpServletRequest request) {
      // model.addAttribute("message", "ABC");
      // InputStream is;
      // OutputStream os;
      // String key;
      // repositoryService.createFileStorage("sow");
      // try {
      // is = new FileInputStream(new java.io.File("D:\\test.txt"));
      // key = repositoryService.saveFile(is);
      // is.close();
      //
      // os = new FileOutputStream(new java.io.File("D:\\test2.txt"));
      // repositoryService.readFile(key, os);
      // os.close();
      // } catch (Exception e) {
      // System.out.println(e);
      // }
      // repositoryService.dropFileStorage("sow");

      // CustomUser user = (CustomUser)
      // SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      // String name = user.getUsername(); // get logged in username
      // System.out.println(name);
      // SecurityUtil.getCurrentClient();
      // String name = request.getParameter("folderName");
      // System.out.println(name);

      return "../../error";
   }

   private String validate(Integer folderID, String name) {
      String prefix = StringUtil.getPrefix(name);
      String fileName = StringUtils.trim(name);

      if (StringUtils.isEmpty(prefix.trim())) {
         return "File's name must not be blank.";
      }

      // check name exists
      if (documentService.fileExists(folderID, fileName)) {
         return "File's name is exists.";
      }
      return "";
   }

}